﻿#include "PageCache.h"

//main函数前创建单例对象
PageCache PageCache::_sInstance;

// 获取⼀个K⻚的span
Span* PageCache::newSpan(size_t k)
{
	assert(k > 0);

	//大于128页，直接向堆申请内存
	if (k > PAGE_SIZE - 1)
	{
		void* ptr = SystemAlloc(k);
		//Span* span = new Span;
		Span* span = _objPool.New();

		//初始化span
		span->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
		span->_pageSize = k;

		//建立映射关系
		//_idSpanMap[span->_pageId] = span;
		_idSpanMap.set(span->_pageId, span);
		return span;
	}


	//先看一下第K个桶有没有Span
	if (!_spanLists[k].Empty())
	{
		Span* kSpan = _spanLists[k].PopFront();
		// 建立id和span的映射，方便central cache回收小块内存时，查找对应的span
		for (PAGE_ID i = 0; i < kSpan->_pageSize; ++i)
		{
			//_idSpanMap[kSpan->_pageId + i] = kSpan;
			_idSpanMap.set(kSpan->_pageId + i, kSpan);

		}

		return kSpan;
	}

	//说明第k个桶没有Span，往后遍历桶
	for (int i = k + 1; i < PAGE_SIZE; i++)
	{
		//找到了
		if (!_spanLists[i].Empty())
		{
			// 从第i个位置桶里获取一个Span，即nSpan
			// 将nSpan的前k页切分下来给kSpan返回给上层
			// 再将切分后的nSpan挂回桶里
			Span* nSpan = _spanLists[i].PopFront();
			//Span* kSpan = new Span;
			Span* kSpan = _objPool.New();

			//切分nSpan
			kSpan->_pageId = nSpan->_pageId;
			kSpan->_pageSize = k;

			nSpan->_pageId += k;
			nSpan->_pageSize -= k;

			//将切分完的nSpan挂回桶里
			_spanLists[nSpan->_pageSize].PushFront(nSpan);


			//存储nSpan的首位页号与nSpan的映射，方便pageCache回收内存时进行合并查找
			//_idSpanMap[nSpan->_pageId] = nSpan; // 首位页号的映射
			_idSpanMap.set(nSpan->_pageId, nSpan);

			//为什么- 1 ？ pageid = 1000 size = 4, 1000, 1001, 1002, 1003
			//_idSpanMap[nSpan->_pageId + nSpan->_pageSize - 1] = nSpan; // 首位页号的映射
			_idSpanMap.set(nSpan->_pageId + nSpan->_pageSize - 1, nSpan);

			
			//建立PAGEID和Span的映射关系，方便centralCache回收小块内存时，找到对应的span
			for (PAGE_ID i = 0; i < kSpan->_pageSize; i++)
			{
				//_idSpanMap[kSpan->_pageId + i] = kSpan;
				_idSpanMap.set(kSpan->_pageId + i, kSpan);

			}

			//返回kSpan
			return kSpan;
		}
	}

	//从k位置后，整个桶都是空的
	//此时，区堆区申请一个128页大小的内存挂到桶里
	//Span* bigSpan = new Span;
	Span* bigSpan = _objPool.New();
	void* ptr = SystemAlloc(PAGE_SIZE - 1);
	bigSpan->_pageSize = PAGE_SIZE - 1;
	bigSpan->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;

	_spanLists[bigSpan->_pageSize].PushFront(bigSpan);

	return newSpan(k);
}

Span* PageCache::MapObjectToSpan(void* obj)
{
	//std::unique_lock<std::mutex>lock(_pageMtx); // RAII锁，出作用域自动释放

	PAGE_ID id = (PAGE_ID)obj >> PAGE_SHIFT; // 地址右移13位就是页号
	//auto ret = _idSpanMap.find(id); //获取id对应的迭代器 
	//if (ret != _idSpanMap.end())
	//{
	//	return ret->second; //返回span对应的地址
	//}
	auto ret = (Span*)_idSpanMap.get(id);
	if (ret != nullptr)
	{
		return ret;
	}
	else //没找到
	{
		assert(false);
		return nullptr;
	}
}

void PageCache::ReleaseSpanToPageCache(Span* span)
{
	//大于128页，直接释放还给堆区
	if (span->_pageSize > PAGE_SIZE - 1)
	{
		void* ptr = (void*)(span->_pageId << PAGE_SHIFT);
		SystemFree(ptr);
		//delete span;
		_objPool.Delete(span);
		return;
	}

	//对当前span 的前后页尝试进行合并，解决内存碎片的问题
	//合并前面的页
	while (1)
	{
		PAGE_ID prevId = span->_pageId - 1;
		//auto ret = _idSpanMap.find(prevId);
		//// 前面相邻页不存在，无法合并
		//if (ret == _idSpanMap.end())
		//	break;
		
		auto ret = (Span*)_idSpanMap.get(prevId);
		if (ret == nullptr)
			break;

		//前面相邻页正在被使用，无法合并
		Span* prevSpan = ret;
		if (prevSpan->_isUse == true)
			break;

		//与前面相邻页的页大小和大于128页，无法合并
		if (prevSpan->_pageSize + span->_pageSize > PAGE_SIZE - 1)
			break;

		//合并
		span->_pageId = prevSpan->_pageId;
		span->_pageSize += prevSpan->_pageSize;
		
		_spanLists[prevSpan->_pageSize].Erase(prevSpan); //从桶里删除
		//delete prevSpan; // 从逻辑结构上释放
		_objPool.Delete(prevSpan);
	}

	//合并后面的页
	while (1)
	{
		PAGE_ID nextId = span->_pageId + span->_pageSize;
		//auto ret = _idSpanMap.find(nextId);
		//// 前面相邻页不存在，无法合并
		//if (ret == _idSpanMap.end())
		//	break;

		auto ret = (Span*)_idSpanMap.get(nextId);
		if (ret == nullptr)
			break;

		//前面相邻页正在被使用，无法合并
		Span* nextSpan = ret;
		if (nextSpan->_isUse == true)
			break;

		//与前面相邻页的页大小和大于128页，无法合并
		if (nextSpan->_pageSize + span->_pageSize > PAGE_SIZE - 1)
			break;

		//合并
		span->_pageSize += nextSpan->_pageSize;

		_spanLists[nextSpan->_pageSize].Erase(nextSpan); //从桶里删除
		//delete nextSpan; // 从逻辑结构上释放
		_objPool.Delete(nextSpan);
	}

	//将span插入到哈希桶里
	_spanLists[span->_pageSize].PushFront(span);
	span->_isUse = false;

	//将新的映射关系写进unordered_map
	/*_idSpanMap[span->_pageId] = span;
	_idSpanMap[span->_pageId + span->_pageSize - 1] = span;*/
	_idSpanMap.set(span->_pageId, span);
	_idSpanMap.set(span->_pageId + span->_pageSize - 1, span);

}

